package com.sx.mvvmlibs.base

import android.app.Application
import android.os.Bundle
import android.os.Handler
import androidx.annotation.CallSuper
import androidx.annotation.MainThread
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
import com.blankj.utilcode.util.ThreadUtils.isMainThread
import com.sx.mvvmlibs.R
import com.sx.mvvmlibs.app.ReposManager
import com.sx.mvvmlibs.base.impl.IViewModel
import com.sx.mvvmlibs.event.UIChangeLiveData
import com.sx.mvvmlibs.http.HttpProcessor
import com.sx.mvvmlibs.http.impl.IBaseResponse
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable
import io.reactivex.rxjava3.functions.Consumer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
import java.lang.reflect.ParameterizedType
import java.lang.reflect.Type


/**
 * @Author: YY
 * @Date: 2022/1/8
 * @Describe: ViewModel 基类
 */
open class BaseViewModel<M : BaseModel>(app: Application) : AndroidViewModel(app), IViewModel {

    /**
     * Model层
     */
    lateinit var mModel: M

    /**
     * 是否自动创建仓库
     */
    private var isAutoCreateRepo = true

    /**
     * 是否缓存自动创建的仓库，默认是 true
     */
    protected open fun isCacheRepo() = true

    /**
     * 管理Rx，针对异步操作造成的内存泄漏
     */
    private lateinit var mCompositeDisposable: Any


    val mUI by lazy { UIChangeLiveData() }


    constructor(app: Application, model: M) : this(app) {
        isAutoCreateRepo = false
        mModel = model
    }

    /**
     * 所有网络请求都在 mCoroutineScope 域中启动协程，当页面销毁时会自动取消
     */
    fun <T> launch(
        block: suspend CoroutineScope.() -> IBaseResponse<T?>?,
        onSuccess: (() -> Unit)? = null,
        onResult: ((t: T) -> Unit),
        onFailed: ((code: Int, msg: String?) -> Unit)? = null,
        onComplete: (() -> Unit)? = null,
    ) {
        viewModelScope.launch {
            try {
                HttpProcessor.handleResult(block(), onSuccess, onResult, onFailed)
            } catch (e: Exception) {
                onFailed?.let { HttpProcessor.handleException(e, it) }
            } finally {
                onComplete?.invoke()
            }
        }
    }

    /**
     * 发起协程
     * TODO：协程和 UI 相关
     */
    fun launchUI(block: suspend CoroutineScope.() -> Unit) {
        viewModelScope.launch { block() }
    }

    /**
     * 发起流
     */
    fun <T> launchFlow(block: suspend () -> T): Flow<T> {
        return flow {
            emit(block())
        }
    }


    /**
     * Rx使用 把订阅管理起来
     */
    fun addSubscribe(disposable: Any) {
        if (!this::mCompositeDisposable.isInitialized) {
            mCompositeDisposable = CompositeDisposable()
        }
        (mCompositeDisposable as CompositeDisposable).add(disposable as Disposable)
    }

    @CallSuper
    override fun onCreate(owner: LifecycleOwner) {
        if (isAutoCreateRepo) {
            if (!this::mModel.isInitialized) {
                val modelClass: Class<M>?
                val type: Type? = javaClass.genericSuperclass
                modelClass = if (type is ParameterizedType) {
                    @Suppress("UNCHECKED_CAST")
                    type.actualTypeArguments[0] as? Class<M>
                } else null
                if (modelClass != null && modelClass != BaseModel::class.java) {
                    mModel = ReposManager.getRepo(modelClass, isCacheRepo())
                }
            }
        }
    }

    override fun onCleared() {
        // 可能Model 为初始化
        if (this::mModel.isInitialized) {
            mModel.onCleared()
        }
        cancelAsyncTask()
    }

    /**
     * 取消异步耗时任务
     */
    private fun cancelAsyncTask() {
        // ViewModel销毁时会执行，同时取消所有异步任务
        if (this::mCompositeDisposable.isInitialized) {
            (mCompositeDisposable as CompositeDisposable).clear()
        }
        viewModelScope.cancel()
    }

    //  ========================== 以下加载框相关 ==========================

    fun showLoadingDialog() {

        showLoadingDialog(getApplication<Application>().getString(R.string.please_wait))
    }

    fun showLoadingDialog(msg: String?) {
        if (isMainThread()) {
            mUI.showLoadingDialogEvent?.value = msg
        } else {
            mUI.showLoadingDialogEvent?.postValue(msg)
        }
    }

    fun dismissLoadingDialog() {
        if (isMainThread()) {
            mUI.dismissLoadingDialogEvent?.call()
        } else {
            mUI.dismissLoadingDialogEvent?.postValue(null)
        }
    }

    //  ========================== 以下启动Activity相关 ==========================

    /**
     * 跳转页面
     * @param clz 所跳转的目的Activity类
     */
    fun startActivity(clz: Class<*>?) {
        startActivity(clz, null)
    }

    /**
     * 跳转页面
     * @param clz    所跳转的目的Activity类
     * @param bundle 跳转所携带的信息
     */
    fun startActivity(clz: Class<*>?, bundle: Bundle?) {
        val params: MutableMap<String, Any> = HashMap()
        params[ParameterField.CLASS] = clz!!
        if (bundle != null) {
            params[ParameterField.BUNDLE] = bundle
        }
        mUI.startActivityEvent?.postValue(params)
    }

    fun startActivityFinish(clz: Class<*>?) {
        startActivityFinish(clz, null)
    }

    fun startActivityFinish(clz: Class<*>?, bundle: Bundle?) {
        val params: MutableMap<String, Any> = HashMap()
        params[ParameterField.CLASS] = clz!!
        if (bundle != null) {
            params[ParameterField.BUNDLE] = bundle
        }
        mUI.startActivityFinishEvent?.postValue(params)
    }

    /**
     * 跳转容器页面
     * @param canonicalName 规范名 : Fragment.class.getCanonicalName()
     */
    fun startContainerActivity(canonicalName: String) {
        startContainerActivity(canonicalName, null)
    }

    open fun startContainerActivity(canonicalName: String, bundle: Bundle?) {
        val params: MutableMap<String, Any> = mutableMapOf()
        params[ParameterField.CANONICAL_NAME] = canonicalName
        if (bundle != null) {
            params[ParameterField.BUNDLE] = bundle
        }
        mUI.startContainerFragmentEvent?.postValue(params)
    }

    @MainThread
    fun finish() {
        mUI.finishEvent?.call()
    }

    object ParameterField {
        var CLASS = "CLASS"
        var BUNDLE = "BUNDLE"
        var CANONICAL_NAME = "CANONICAL_NAME"
    }

}